﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.Xml;
using System.Globalization;
using GeoRef.Properties;
using System.Runtime.Serialization;

namespace GeoRef
{
    /// <summary>
    /// Data representing type of vehicle
    /// </summary>
    public enum VehicleType : int
    {
        /// <summary>
        /// Any four-wheeled vehicle
        /// </summary>
        car = 0,

        /// <summary>
        /// Any type of motorized bike
        /// </summary>
        motorbike,

        /// <summary>
        /// Bicycles
        /// </summary>
        cycle,

        /// <summary>
        /// Trains
        /// </summary>
        train,

        /// <summary>
        /// Any type of ship (i.e. private yachts, cruise ships, cargo containers...)
        /// </summary>
        ship,

        /// <summary>
        /// Airplanes
        /// </summary>
        plane
    };

    /// <summary>
    /// Represents a route composed of two location
    /// </summary>
    [DataContract]
    [KnownTypeAttribute(typeof(GeoLoc))]
    [KnownTypeAttribute(typeof(GeoPoint))]
    public class GeoRoute
    {
        #region GeoRoute field
        /// <value>
        /// ID of current route
        /// </value>
        [DataMember]
        //private KeyValuePair<int, short> id;
        private int id;

        /// <value>
        /// Start location of route
        /// </value>
        [DataMember]
        private GeoLoc start;

        ///<value>
        ///Stop location of route
        ///</value>
        [DataMember]
        private GeoLoc end;

        /// <value>
        /// List of viaPoints for the route
        /// </value>
        [DataMember]
        private List<GeoLoc> viaPoints;

        /// <value>
        /// List of points that describe the route's polyline
        /// </value>
        //[DataMember]
        private List<GeoPoint> polyline;

        /// <value>
        /// List of locations for driving instructions
        /// </value>
        [DataMember]
        private List<GeoLoc> instructions;

        /// <value>
        /// The type of the vehicle used for the route (e.g train, car, plane)
        /// </value>
        [DataMember]
        private VehicleType vehicle;

        /// <value>
        /// The id of the vehicle used for the route
        /// </value>
        [DataMember]
        private int vehicleId;

        /// <value>
        /// Spent time from start to end of current route,
        /// expressed in minutes
        /// </value>
        [DataMember]
        private short time;

        /// <value>
        /// Covered distance from start to end of current route,
        /// expressed in kilometers
        /// </value>
        [DataMember]
        private double distance;

        /// <value>
        /// Number of busy places
        /// </value>
        [DataMember]
        private int passengers;

        /// <value>
        /// True if all data is readily available (polyline and driving
        /// instructions), false if some data must be retrieved from 
        /// the internet or the database
        /// </value>
        [DataMember]
        private bool consistent;

        #endregion

        #region GeoRoute Constructor
        /// <summary>
        /// Initializes a new GeoRoute object, setting car
        /// as default vehicle
        /// </summary>
        /// <param name="start">Start location</param>
        /// <param name="stop">Stop location</param>
        /// <exception cref="System.ArgumentException">
        /// If start is equal to stop</exception>
        public GeoRoute(GeoLoc start, GeoLoc stop) {            
            if (start == stop) {
                throw new System.ArgumentException();
            } else {
                this.start = start;
                this.end = stop;
                this.viaPoints = new List<GeoLoc>();
                this.instructions = new List<GeoLoc>();
                this.polyline = new List<GeoPoint>();
                this.consistent = false;
                this.distance = 0;
                this.time = 0;
                this.passengers = 0;
                this.vehicle = VehicleType.car;
                //this.id = new KeyValuePair<int,short>(-1, -1);
                this.id = -1;
            }
        }

        #endregion

        #region GeoRoute Properties

        /// <summary>
        /// Gets or sets route id
        /// </summary>
        //public KeyValuePair<int, short> Id
        public int Id
        {
            get { return id; }
            set { id = value; }              
        }

        /// <summary>
        /// Gets or sets the start location
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// If start is equal to stop</exception>
        public GeoLoc Start {
            get { return start; }
            set {
                if (value != this.end) {
                    this.consistent = false;
                    start = value;
                } else {
                    throw new System.ArgumentException("Start and end must be two different points");
                }
            }
        }

        /// <summary>
        /// Gets or sets the end location
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// If start is equal to stop</exception>
        public GeoLoc End
        {
            get { return end; }
            set {
                if (value != this.start) {
                    this.consistent = false;
                    end = value;
                } else {
                    throw new System.ArgumentException("Start and end must be two different points");
                }
            }
        }

        /// <summary>
        /// Describes covered distance of route in km
        /// </summary>
        /// <permission cref="System.Security.PermissionSet">
        /// Public Get
        /// Protected Set
        /// </permission>
        /// <exception cref="FormatException">
        /// If sets a negative value
        /// </exception>
        public double Distance
        {
            get { return distance; }
            internal set {
                if (value >= 0) {
                    distance = value;
                } else {
                    throw new FormatException("Distance can't be negative");
                }
            }
        }

        /// <summary>
        /// Gets covered distance of route in miles
        /// </summary>
        public double DistanceInMiles {
            get { return distance / 1.609344; }            
        }

        /// <summary>
        /// Gets or sets the vehicle type used for the route
        /// </summary>
        public VehicleType Vehicle {
            get { return vehicle; }
            set { vehicle = value; }
        }

        /// <summary>
        /// Gets or sets the id of the vehicle used for the route
        /// </summary>
        public int VehicleId {
            get { return vehicleId; }
            set { vehicleId = value; }
        }

        /// <summary>
        /// Describes spent time for route
        /// </summary>
        /// <permission cref="System.Security.PermissionSet">
        /// Public Get
        /// Protected Set
        /// </permission>
        /// <exception cref="FormatException">
        /// If sets a negative value
        /// </exception>
        public short Time {
            get { return time; }
            internal set {
                if (value >= 0) {
                    time = value;
                } else {
                    throw new FormatException("Time can't be negative");
                }
            }
        }

        /// <summary>
        /// Gets or sets number of busy places
        /// </summary>
        /// <exception cref="FormatException">
        /// If sets a negative value
        /// </exception>
        public int Passengers {
            get { return passengers; }
            set {
                if (value >= 0) {
                    passengers = value;
                } else {
                    throw new FormatException("Passengers number can't be negative");
                }
            }
        }

        /// <summary>
        /// Gets viaPoint list
        /// </summary>
        public List<GeoLoc> ViaPoints {
            get { return viaPoints; }
            internal set { viaPoints = value; }
        }
		
        /// <summary>
        /// Gets the polygonal line defining the whole route
        /// from start to end
        /// </summary>
		public List<GeoPoint> PolyLine {
			get {return polyline;}
            internal set { polyline = value; }
		}

        /// <summary>
        /// True if all necessary data is readily available,
        /// false if some data is missing
        /// </summary>
        public bool IsConsistent
        {
            get { return consistent; }
        }

        /// <summary>
        /// The LineString is a string representation of the PolyLine
        /// compatible with the corresponding type in SQL Server 2008
        /// </summary>
        public string PolylineAsLineString
        {
            get
            {
                if (this.polyline.Count == 0) {
                    return null;
                }
                StringBuilder sb = new StringBuilder();
                if (this.Vehicle < VehicleType.train) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "{0:G} {1:G}, ", this.Start.Lat, this.Start.Lng));
                    sb.Append(this.CreateLinestring(this.polyline));
                    sb.Append(string.Format(CultureInfo.InvariantCulture, ", {0:G} {1:G}", this.End.Lat, this.End.Lng));
                } else {
                    sb.Append(this.CreateLinestring(this.polyline));
                }
                return (sb.ToString());
            }
        }

        /// <summary>
        /// MultiPoint representation for viaPoints list. 
        /// The MultiPoint is a string representation of the PolyLine
        /// compatible with the corresponding type in SQL Server 2008
        /// </summary>
        public string ViaPointsAsMultiPoint {
            get {
                return (this.CreateMultipoint(this.viaPoints));
            }                
        }

        /// <summary>
        /// MultiPoint representation for polyline list. 
        /// The MultiPoint is a string representation of the PolyLine
        /// compatible with the corresponding type in SQL Server 2008
        /// </summary>
        public string PolyLineAsMultiPoint {
            get {
                if (this.polyline.Count == 0) {
                    return null;
                }
                StringBuilder sb = new StringBuilder();
                if (this.Vehicle < VehicleType.train) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "({0:G} {1:G}), ", this.Start.Lat, this.Start.Lng));
                    sb.Append(this.CreateMultipoint(this.polyline));
                    sb.Append(string.Format(CultureInfo.InvariantCulture, ", ({0:G} {1:G})", this.End.Lat, this.End.Lng));
                } else {
                    sb.Append(this.CreateMultipoint(this.polyline));
                }
                return (sb.ToString());
            }
        }

        /// <summary>
        /// Gets all driving instructions
        /// Driving Instructions are only those points in the route that represent
        /// a significant change in direction for the driver. They come
        /// with a human-readable description of such change.
        /// </summary>
        public List<GeoLoc> Instructions {
            get { return instructions; }
            internal set { instructions = value; }
        }

        #endregion

        #region Protected Internal GeoRoute Methods

        /// <summary>
        /// Removes all points from polyline
        /// </summary>
        protected internal void ClearPolyline() {
            this.polyline.Clear();
            consistent = false;
        }

        /// <summary>
        /// Inserts a new point at the end of the polyline        
        /// </summary>
        /// <param name="point">point to insert</param>
        protected internal void InsertInPolyline(GeoPoint point) {
            this.polyline.Insert(this.polyline.Count, point);
            consistent = false;
        }

        /// <summary>
        /// Insert in polyline a set of points
        /// </summary>
        /// <param name="polyline">String formed by a list of points
        /// separated by spaces, where each one is in the format 
        /// "latitude,longitude,0.0"
        /// </param>
        /// <example>
        /// This code shows an example of how to set a polyline
        /// from point 1.3,23.4 to point 2.4,54.3
        /// <code>
        /// this.setPolyline("1.3,23.4,0.0 2.4,54.3,0.0");
        /// </code>
        /// </example>
        /// <seealso cref="GeoRef.GeoPoint"/>
        protected internal void SetPolyline(string polyline) {
            string[] tmp;

            //split using a white space
            tmp = polyline.Split(' ');
            foreach (string i in tmp) {
                if (i.Contains(",")) {
                    String[] tmpCoords = i.Split(',');
                    this.InsertInPolyline(new GeoPoint(tmpCoords[1] + "," + tmpCoords[0]));
                }
            }
            consistent = false;
        }

        /// <summary>
        /// Updates both instructions and polyline points,
        /// retrieving informations from google maps, if
        /// vehicle is car or cycle or motorbike. Otherwise
        /// sets polyline as straight from the start to the end
        /// of routes
        /// </summary>
        /// <exception cref="System.Xml.XmlException">
        /// In case of parse error, usually due to a path query that
        /// Google is unable to resolve
        /// </exception>
        protected internal void Update() {
            string[] msg = new string[2];
            if (this.vehicle > VehicleType.cycle) {
                this.viaPoints.Clear();
                this.polyline.Clear();
                this.polyline.Add(start);
                this.polyline.Add(end);
                this.instructions.Clear();
                
                if (this.vehicle == VehicleType.plane) {
                    msg[0] = "Take the airplane";
                    msg[1] = "Go out from the airplane";
                } else if (this.vehicle == VehicleType.ship) {
                    msg[0] = "Take the ship";
                    msg[1] = "Go out from the ship";
                } else if (this.vehicle == VehicleType.train) {
                    msg[0] = "Catch the train";
                    msg[1] = "Go out of the train";
                }
                this.instructions.Add(new GeoLoc(start, "", msg[0]));
                this.instructions.Add(new GeoLoc(end, "", msg[1]));
                this.CalculateDistance();

            } else { //case of car, motorbike, cycle
                XmlReader reader;
                string[] values;
                string valueTrip;
                XmlDocument doc = new XmlDocument();

                string serviceUrl = Settings.Default.RouteRequestWebServiceUrl;
                                
                this.instructions.Clear();
                this.polyline.Clear();
                consistent = false;

                List<string> urlList = new List<string>();
                if (viaPoints.Count == 0) {
                    urlList.Add(serviceUrl + "&saddr=@" + start.ToString() + "&daddr=@" + end.ToString());
                } else if (viaPoints.Count == 1) {
                    urlList.Add(serviceUrl + "&saddr=@" + start.ToString() + "&daddr=@" + viaPoints.ElementAt(0).ToString());
                    urlList.Add(serviceUrl + "&saddr=@" + viaPoints.ElementAt(0).ToString() + "&daddr=@" + end.ToString());
                } else {
                    urlList.Add(serviceUrl + "&saddr=@" + start.ToString() + "&daddr=@" + viaPoints.ElementAt(0).ToString());
                    for (int i = 0; i < viaPoints.Count - 1; i++) {
                        urlList.Add(serviceUrl + "&saddr=@" + viaPoints.ElementAt(i).ToString() + "&daddr=@" + viaPoints.ElementAt(i + 1).ToString());
                    }
                    urlList.Add(serviceUrl + "&saddr=@" + viaPoints.ElementAt(viaPoints.Count - 1).ToString() + "&daddr=@" + end.ToString());
                }
                for (int u = 0; u < urlList.Count; u++) {
                    /* Could throw a: 
                     * ArgumentNullException
                     * SecurityException (insufficient permission to access to data location)
                     * FileNotFoundException
                     * UriFormatException
                     */
                    reader = XmlReader.Create(urlList.ElementAt(u));

                    /* Could throw a:
                     * XmlException (often parse error)
                     */
                    doc.Load(reader);

                    XmlNode root = doc.DocumentElement;
                    /* Removes first "name" tag to keep consistent pairs of 'name' and 
                     * 'coordinates' tags 
                     */
                    root.FirstChild.RemoveChild(root.FirstChild.FirstChild);
                    /* Retrieves informations about names and coordinates and 
                     * uses them to build instruction list
                     */
                    XmlNodeList names = doc.GetElementsByTagName("name");
                    XmlNodeList coords = doc.GetElementsByTagName("coordinates");
                    for (int i = 0; i < names.Count - 1; i++) {
                        /* Note: this is due to the strange Google maps behavior, that
                         * accepts coordinates in form <lat,lng> but returns search 
                         * results in form <lng,lat> in xml document
                         */
                        String[] tmpCoord = coords.Item(i).InnerText.Split(',');
                        instructions.Add(new GeoLoc(tmpCoord[1] + "," + tmpCoord[0], "", names.Item(i).InnerText));
                    }
                    if (viaPoints.Count > 0 && u < urlList.Count - 1) {
                        instructions.RemoveAt(instructions.Count - 1);
                    }
                    XmlNodeList lines = doc.GetElementsByTagName("LineString");
                    foreach (XmlNode n in lines) {
                        this.SetPolyline(n.InnerText);
                    }
                    XmlNodeList descr = doc.GetElementsByTagName("description");
                    //substitutes special html characters used for whitespace, replaces brackets with space and then splits by spaces
                    valueTrip = HttpUtility.HtmlDecode(descr.Item(descr.Count - 1).InnerText.Replace("&#160;", " ").Replace('(', ' ').Replace(')', ' '));
                    values = valueTrip.Split(' ');
                    //Calculates distance in km
                    if (values[2].CompareTo("km") == 0) {
                        this.distance += System.Double.Parse(values[1], CultureInfo.InvariantCulture);
                    } else {
                        this.distance += System.Double.Parse(values[1], CultureInfo.InvariantCulture) / 1000;
                    }
                    //calculates time in minutes, summing days, hours and minutes
                    for (int i = 6; i < values.Length; i = i + 2) {
                        if ((Array.IndexOf(values, "day", i) != -1) ||
                            (Array.IndexOf(values, "days", i) != -1)) {
                            //number of days multiplied for 1440 minutes in a day                        
                                this.time += (short)((Convert.ToInt16(values[i - 1])) * 1440);
                        } else if ((Array.IndexOf(values, "hour", i) != -1) ||
                                   (Array.IndexOf(values, "hours", i) != -1)) {
                            //number of hours multipled for 60 minutes in a hour
                            this.time += (short)((Convert.ToInt16(values[i - 1])) * 60);
                        } else if ((Array.IndexOf(values, "min", i) != -1) ||
                                   (Array.IndexOf(values, "mins", i) != -1)) {
                            //number of minutes
                            this.time += Convert.ToInt16(values[i - 1]);
                        }
                    }
                }
            }
            consistent = true;
        }

        /// <summary>
        /// Removes a specific point from polyline list
        /// </summary>
        /// <param name="point">Specific point to remove</param>
        protected internal void RemoveFromPolyline(GeoPoint point) {
            polyline.Remove((GeoPoint)point);
            consistent = false;
        }
        

        #endregion

        #region Public GeoRoute Methods

        /// <summary>
        /// Checks for and updates route consistency, that is
        /// same point in start, first point of polyline and 
        /// driving instructions and same point in end, last point
        /// of polyline and driving instructions.
        /// </summary>
        /// <returns>Returns true if there is route consistency, false
        /// otherwise</returns>
        public bool CheckConsistency() {
            if (this.vehicle >= VehicleType.train) {//there is a straight line
                this.consistent = ((this.instructions.Count == 2) &&
                                   (this.polyline.Count == 2) &&
                                   (this.start == this.instructions.First()) &&
                                   (this.start == this.polyline.First()) &&
                                   (this.end == this.instructions.Last()) &&
                                   (this.end == this.polyline.Last()));
                return this.consistent;
            } else {
                this.consistent = ((this.instructions.Count > 1) &&
                                   (this.polyline.Count > 1) &&
                                   (Math.Round(this.instructions.First().Lat, 5, MidpointRounding.AwayFromZero) == Math.Round(this.polyline.First().Lat, 5, MidpointRounding.AwayFromZero)) &&
                                   (Math.Round(this.instructions.First().Lng, 5, MidpointRounding.AwayFromZero) == Math.Round(this.polyline.First().Lng, 5, MidpointRounding.AwayFromZero)) &&
                                   (Math.Round(this.instructions.Last().Lng, 5, MidpointRounding.AwayFromZero) == Math.Round(this.polyline.Last().Lng, 5, MidpointRounding.AwayFromZero)) &&
                                   (Math.Round(this.instructions.Last().Lat, 5, MidpointRounding.AwayFromZero) == Math.Round(this.polyline.Last().Lat, 5, MidpointRounding.AwayFromZero)));
                return this.consistent;
            }
        }

        /// <summary>
        /// Removes all viaPoints
        /// </summary>
        public void ClearViaPoints() {
            this.viaPoints.Clear();
            consistent = false;
        }

        /// <summary>
        /// Removes a specific point from viaPoints list
        /// </summary>
        /// <param name="point">Specific point to remove</param>
        public void RemoveViaPoint(GeoPoint point) {            
            viaPoints.Remove((GeoLoc)point);
            consistent = false;
        }       

        /// <summary>
        /// Inserts a new viaPoint at the end of the list
        /// if not already present
        /// </summary>
        /// <param name="point">viaPoint to insert</param>
        public void InsertViaPoint(GeoLoc point) {            
            if  ((this.viaPoints.FindIndex(delegate(GeoLoc l) { return (l == point); }))== -1) {
                this.viaPoints.Insert(this.viaPoints.Count, point);
            }            
            //if present do nothing
            consistent = false;
        }

        /// <summary>
        /// Inserts a new viaPoint before beforePoint
        /// </summary>
        /// <param name="point">viaPoint to insert</param>
        /// <param name="beforePoint">viaPoint before of which insert</param>
        public void InsertViaPoint(GeoLoc point, GeoLoc beforePoint) {
            int index;
            index = this.viaPoints.FindIndex(delegate(GeoLoc l) { return (l == beforePoint);});
            if (index != -1) {
                this.viaPoints.Insert(index, point);
                consistent = false;
            }
        }

        /// <summary>
        /// Inserts a new viaPoint before beforeindex
        /// </summary>
        /// <param name="point">viaPoint to insert</param>
        /// <param name="beforeIndex">viaPoint index before of which insert</param>
        /// <exception cref="System.ArgumentOutOfRangeException">If beforeIndex is out of list bounds</exception>
        public void InsertViaPoint(GeoLoc point, int beforeIndex) {
            this.viaPoints.Insert(beforeIndex, point);
            consistent = false;
        }

        /// <summary>
        /// Determines if two GeoRoutes have same value, that
        /// is same start, end and viaPoints list
        /// </summary>
        /// <param name="r1">first GeoRoute to compare</param>
        /// <param name="r2">second GeoRoute to compare</param>
        /// <returns>True if r1 is equal to r2, false otherwise
        /// </returns>
        public static bool operator ==(GeoRoute r1, GeoRoute r2) {
            if ((r1.Start == r2.Start) &&
                (r1.End == r2.End) &&
                (r1.ViaPoints.Equals(r2.ViaPoints) == true)) {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Determines if two GeoRoutes have different value,that
        /// is different start and/or different end and/or
        /// different viaPoints list
        /// </summary>
        /// <param name="r1">first GeoRoute to compare</param>
        /// <param name="r2">second GeoRoute to compare</param>
        /// <returns>True if r1 is different from r2, false otherwise
        /// </returns>
        public static bool operator !=(GeoRoute r1, GeoRoute r2) {
            if ((r1.Start == r2.Start) &&
                (r1.End == r2.End) &&
                (r1.ViaPoints.Equals(r2.ViaPoints) == true)) {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Determines whether this instance of GeoRoute and a
        /// specificed object obj, which must also be a GeoRoute
        /// object, have the same value
        /// </summary>
        /// <param name="obj">Object to compare</param>
        /// <returns>True if this is equal to obj, false otherwise</returns>
        public override bool Equals(object obj) {
            if (!(obj is GeoRoute)) {
                return false;
            } else {
                return this == (GeoRoute)obj;
            }
        }

        /// <summary>
        /// Default HashCoding
        /// </summary>
        /// <returns>The hash coding of the object</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion

        #region GeoRoute Private Method

        /// <summary>
        /// Calculates a geodetic distance between two
        /// coordinates at sea level. Used to express 
        /// approximated distance when type of vehicle
        /// is not terrestrial, e.g. plane, train or ship
        /// </summary>
        private void CalculateDistance() {
            double radiusEarth = 6371;
            double latSt, latEnd, lngSt, lngEnd;
            double fi, p;
            //Converts from degrees to radian
            latSt = Math.PI * this.start.Lat / 180;
            latEnd = Math.PI * this.end.Lat / 180;
            lngSt = Math.PI * this.start.Lng / 180;
            lngEnd = Math.PI * this.end.Lng / 180;
            //Calculates angle between two points
            fi = Math.Abs(lngSt - lngEnd);
            p = Math.Acos(Math.Sin(latEnd) * Math.Sin(latSt) + Math.Cos(latEnd) * Math.Cos(latSt) * Math.Cos(fi));
            this.distance = p * radiusEarth;
        }

        /// <summary>
        /// Creates a multipoint string representation
        /// of given List&lt;<see cref="GeoPoint"/>
        /// </summary>
        /// <param name="lists">List to convert</param>
        /// <returns>Multipoint string version of List&lt;<see cref="GeoPoint"/></returns>
        private string CreateMultipoint(List<GeoPoint> lists) {
            StringBuilder sb = new StringBuilder();
            if (lists.Count > 0) {
                foreach (GeoPoint gp in lists) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "({0:G} {1:G}), ", gp.Lat, gp.Lng));
                }
                sb.Remove(sb.Length - 2, 2);
                return sb.ToString();
            } else {
                return null;
            }
            
        }


        /// <summary>
        /// Serializes the given List&lt;<see cref="GeoLoc"/>&gt; in
        /// MultiPoint format
        /// </summary>
        /// <param name="lists">The List&lt;GeoLoc&gt; to convert</param>
        /// <returns>LineString representation of the given list</returns>
        private string CreateMultipoint(List<GeoLoc> lists) {
            StringBuilder sb = new StringBuilder();
            if (lists.Count > 0) {
                foreach (GeoLoc gp in lists) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "({0:G} {1:G}), ", gp.Lat, gp.Lng));
                }
                sb.Remove(sb.Length - 2, 2);
                return sb.ToString();
            } else {
                return null;
            }
        }

        /// <summary>
        /// Serializes the given List&lt;<see cref="GeoLoc"/>&gt; in
        /// LineString format
        /// </summary>
        /// <param name="lists">The List&lt;GeoLoc&gt; to convert</param>
        /// <returns>LineString representation of the given list</returns>
        private string CreateLinestring(List<GeoLoc> lists) {
            StringBuilder sb = new StringBuilder();
            if (lists.Count > 0) {
                foreach (GeoLoc gp in lists) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "{0:G} {1:G}, ", gp.Lat, gp.Lng));
                }
                sb.Remove(sb.Length - 2, 2);
                return sb.ToString();
            } else {
                return null;
            }
        }


        /// <summary>
        /// Serializes the given List&lt;<see cref="GeoPoint"/>&gt; in
        /// LineString format
        /// </summary>
        /// <param name="lists">The List&lt;GeoPoint&gt; to convert</param>
        /// <returns>LineString representation of the given list</returns>
        private string CreateLinestring(List<GeoPoint> lists) {
            StringBuilder sb = new StringBuilder();
            if (lists.Count > 0) {
                foreach (GeoPoint gp in lists) {
                    sb.Append(string.Format(CultureInfo.InvariantCulture, "{0:G} {1:G}, ", gp.Lat, gp.Lng));
                }
                sb.Remove(sb.Length - 2, 2);
                return sb.ToString();
            } else {
                return null;
            }

        }

        #endregion

    }
}
